iT邦幫忙

2023 iThome 鐵人賽

DAY 4
1
Mobile Development

App從開發到上架系列 第 4

Day4: iOS 開發:基本畫面建構 - BaseViewController

  • 分享至 

  • xImage
  •  

我們要建立一個檔案叫做BaseViewController,點開專案 -> New File -> 選擇Swift檔案並命名為 BaseViewController.swift -> Enter

之後我們建立的ViewController都會繼承BaseViewController,這樣做的意義在於幫助我把程式碼變得更乾淨簡潔!

以下是簡單的跳頁例子:

self.navigationController?.pushViewController(nextVC, animated: animated)

應該都有印象吧?麻煩的點在於,我每次要跳頁,都要寫這一段,如果我寫成一個func,需要用到時把他呼叫出來是不是就方便很多?

public func pushViewController(nextVC: UIViewController, animated: Bool = true) {
    self.navigationController?.pushViewController(nextVC, animated: animated)
}

把上面的寫法改成呼叫func的方式並且讓其他畫面去繼承我的BaseViewController,我要換頁的時候只需要:

pushViewController(nextVC: 你要的ViewController)

事情就簡單很多了,程式碼也會比較乾淨對吧?

接下來把需要用到的都寫進去吧!

/// NavigationController.pushViewController 跳頁 (不帶 Closure)
///
/// 一般常見的 self.navigationController.pushViewController
///
/// - Parameters:
///   - nextVC: 要跳頁到的 UIViewController
///   - animated: 是否要換頁動畫,預設為 true
public func pushViewController(nextVC: UIViewController, animated: Bool = true) {
    self.navigationController?.pushViewController(nextVC, animated: animated)
}
/// NavigationController.pushViewController 跳頁 (不帶 Closure)
///
/// 用 UIApplication.shared.connectScenes 裡面第一個是 keyWindow 的 rootViewController 來當作 UINavigationController,來進行 pushViewController
///
/// - Parameters:
///   - viewController: 要跳頁到的 UIViewController
///   - animated: 是否要換頁動畫,預設為 true
public func pushViewController(_ viewController: UIViewController, animated: Bool = true) {
    if let navigationController = UIApplication.shared
        .connectedScenes
        .compactMap({ $0 as? UIWindowScene })
        .flatMap({ $0.windows })
        .first(where: { $0.isKeyWindow })?
        .rootViewController as? UINavigationController {
        navigationController.pushViewController(viewController, animated: animated)
    }
}
/// NavigationController.pushViewController 跳頁 (帶 Closure)
/// - Parameters:
///   - viewController: 要跳頁到的 UIViewController
///   - animated: 是否要換頁動畫
///   - completion: 換頁過程中,要做的事
public func pushViewController(_ viewController: UIViewController,
                               animated: Bool,
                               completion: @escaping () -> Void) {
    self.navigationController?.pushViewController(viewController, animated: animated)
    guard animated, let coordinator = transitionCoordinator else {
        DispatchQueue.main.async { completion() }
        return
    }
    coordinator.animate(alongsideTransition: nil) { _ in completion() }
}

/// NavigationController.pushViewController 跳頁時刪掉舊的畫面 (不帶 Closure)
/// - Parameters:
///   - currentVC: 現在的 UIViewController
///   - nextVC: 要跳頁到的 UIViewController
///   - animated: 是否要換頁動畫
public func pushViewController(currentVC: UIViewController,
                               nextVC: UIViewController,
                               animated: Bool) {
    navigationController?.pushViewController(nextVC, animated: true)

    guard self.navigationController != nil &&
            self.navigationController?.viewControllers != nil else {
        return
    }

    // 刪除上一個畫面
    let arrayVC = NSMutableArray(array: (self.navigationController?.viewControllers)!)
    for vc in arrayVC {
        if (vc as! UIViewController) == currentVC  {
            arrayVC.remove(vc)
            break
        }
    }

    self.navigationController?.viewControllers = arrayVC as! [UIViewController]
}
/// NavigationController.pushViewController 跳頁時刪掉舊的畫面 (帶 Closure)
/// - Parameters:
///   - currentVC: 現在的 UIViewController
///   - nextVC: 要跳頁到的 UIViewController
///   - animated: 是否要換頁動畫
///   - completion: 換頁過程中,要做的事
public func pushViewController(currentVC: UIViewController,
                               nextVC: UIViewController,
                               animated: Bool,
                               completion: @escaping () -> Void) {
    navigationController?.pushViewController(nextVC, animated: true)

    guard self.navigationController != nil &&
            self.navigationController?.viewControllers != nil else {
        return
    }

    // 刪除上一個畫面
    let arrayVC = NSMutableArray(array: (self.navigationController?.viewControllers)!)
    for vc in arrayVC {
        if (vc as! UIViewController) == currentVC  {
            arrayVC.remove(vc)
            break
        }
    }

    self.navigationController?.viewControllers = arrayVC as! [UIViewController]

    guard animated, let coordinator = transitionCoordinator else {
        DispatchQueue.main.async {
            completion()
        }
        return
    }

    coordinator.animate(alongsideTransition: nil) { _ in
        completion()
    }
}
/// NavigationController.popViewController 回上一頁 (不帶 Closure)
/// - Parameters:
///   - animated: 是否要換頁動畫,預設為 true
public func popViewController(_ animated: Bool = true) {
    self.navigationController?.popViewController(animated: animated)
}
/// NavigationController.popViewController 回上一頁 (帶 Closure)
/// - Parameters:
///   - animated: 是否要換頁動畫
///   - completion: 換頁過程中,要做的事
public func popViewController(animated: Bool, completion: @escaping () -> Void) {
    self.navigationController?.popViewController(animated: animated)
    guard animated, let coordinator = transitionCoordinator else {
        DispatchQueue.main.async { completion() }
        return
    }
    coordinator.animate(alongsideTransition: nil) { _ in completion() }
}
/// NavigationController.popToViewController 回到指定 ViewController (不帶 Closure)
/// - Parameters:
///   - currentVC: 目前所在的 ViewController
///   - popVC_index: 在 NavigationController.viewControllers 中,指定 ViewController 的 index
///   - animated: 是否要換頁動畫,預設為 true
public func popToViewController(currentVC viewController: UIViewController,
                                popVC_index: Int,
                                animated: Bool = true) {
    guard let currentVC_index = navigationController?.viewControllers.firstIndex(of: self) else { return }
    if let vc = navigationController?.viewControllers[currentVC_index - popVC_index] {
        self.navigationController?.popToViewController(vc, animated: animated)
    }
}
/// NavigationController.popToViewController 回到指定 ViewController (不帶 Closure)
///
/// 一般常見的 self.navigationController?.popToViewController
///
/// - Parameters:
///   - viewController: 要 pop 回去的 ViewController
///   - animated: 是否要換頁動畫,預設為 true
public func popToViewController(_ viewController: UIViewController,
                                animated: Bool = true) {
    self.navigationController?.popToViewController(viewController, animated: animated)
}
/// NavigationController.popToViewController 回到指定 ViewController (帶 Closure)
/// - Parameters:
///   - viewController: 要 pop 回去的 ViewController
///   - animated: 是否要換頁動畫
///   - completion: 換頁過程中,要做的事
public func popToViewController(_ viewController: UIViewController,
                                animated: Bool,
                                completion: @escaping () -> Void) {
    self.navigationController?.popToViewController(viewController, animated: animated)
    guard animated, let coordinator = transitionCoordinator else {
        DispatchQueue.main.async { completion() }
        return
    }
    coordinator.animate(alongsideTransition: nil) { _ in completion() }
}
/// NavigationController.popToRootViewController 回到 Root ViewController
/// - Parameters:
///   - animated: 是否要換頁動畫,預設為 true
public func popToRootViewController(_ animated: Bool = true) {
    self.navigationController?.popToRootViewController(animated: animated)
}
/// NavigationController.dismiss (帶 Closure)
/// - Parameters:
///   - animated: 是否要關閉動畫,預設為 true
///   - completion: 關閉過程中,要做的事,預設為 nil
public func dismissViewController(_ animated: Bool = true, completion: (()-> Void)? = nil) {
    self.navigationController?.dismiss(animated: animated, completion: completion)
}
/// ViewController.popUp (帶 Closure)
/// - Parameters:
///   - viewController: 要彈出的 ViewController
///   - modalPresentationStyle: UIModalPresentationStyle,預設為 .overFullScreen
///   - modalTransitionStyle: UIModalTransitionStyle,預設為 .coverVertical
///   - animated: 是否要彈出動畫,預設為 true
///   - completion: 彈出過程中,要做的事,預設為 nil
public func popUpViewController(viewController: UIViewController,
                                modalPresentationStyle: ModalPresentationStyle = .overFullScreen,
                                modalTransitionStyle: ModalTransitionStyle = .coverVertical,
                                animated: Bool = true,
                                completion: (()-> Void)? = nil) {
    viewController.modalPresentationStyle = modalPresentationStyle
    viewController.modalTransitionStyle = modalTransitionStyle
    self.present(viewController, animated: animated, completion: completion)
}
/// ViewController.dismissPopUp
/// - Parameters:
///   - animated: 是否要關閉動畫,預設為 true
///   - completion: 關閉過程中,要做的事,預設為 nil
public func dismissPopUpViewController(_ animated: Bool = true, completion: (()-> Void)? = nil) {
    self.dismiss(animated: animated, completion: completion)
}
/// 清除 NavigationStack 中除了下一個畫面以外的畫面
/// - Parameters:
///   - viewController: 下一個畫面的 ViewController
public func cleanViewControllers(viewController: UIViewController) {
    guard viewController.navigationController?.viewControllers != nil else {
        return
    }
    var vcs = viewController.navigationController!.viewControllers
    for _ in 0 ..< vcs.count - 1 {
        vcs.remove(at: 0)
    }
    DispatchQueue.main.async {
        viewController.navigationController!.viewControllers = vcs
    }
}

以上就是今天的內容!明天見!


上一篇
Day 3 專案架構介紹
下一篇
Day5: iOS 開發:客製化元件 - CustomTabBarView
系列文
App從開發到上架30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言